package com.googlecode.keepwalking;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.SensorManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;

public class WalkingDetector extends Service {
	private static final String TAG = "WalkingDetector";
	public static final String ACTION_START = "com.googlecode.keepwalking.ACTION_START";
	public static final String ACTION_STOP = "com.googlecode.keepwalking.ACTION_STOP";
	private static float alpha = 0.5f;
	private static int sampling = 20;
	private static int interval = 1000 * 10;
	private static float threshold = 0.5f;
	private volatile long timestamp = System.currentTimeMillis();
	private volatile float acceleration = 0.0f;
	private volatile float stddev = 0.0f;
	private volatile boolean fWalking = false;
	private long walkingStart;
	private SensorDataFetcher fetcher;
	private WalkingTracker tracker;
	private boolean fRunning = false;
	private Thread thread;
	
	private final IBinder binder = new WalkingDetectorBinder();
	
	public class WalkingDetectorBinder extends Binder {
		public WalkingDetector getService() {
			return WalkingDetector.this;
		}
	}
	
	public void onCreate() {
		Log.i(TAG, "onCreate");
		fetcher = new SensorDataFetcher(this);
		tracker = new WalkingTracker(this);
	}
	
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, "onStartCommand");
		if(intent != null) {
			Log.d(TAG, "got intent:" + intent.getAction());
			if(intent.getAction().equals(ACTION_START)) {
				startDetect();
			} else if(intent.getAction().equals(ACTION_STOP)) {
				stopDetect();
			} else {
				throw new IllegalArgumentException("invalid action");
			}
		}
		return START_STICKY;
	}
	
	public synchronized void startDetect() {
		if(!fRunning) {
			thread = new Thread() {
				public void run() {
					PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
					PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WalkingDetector");
					wl.acquire();
					while(fRunning) {
						fetcher.start(sampling, SensorManager.SENSOR_DELAY_NORMAL);
						try {
							Thread.sleep(interval);
						} catch(InterruptedException e) {
						}
					}
					wl.release();
				}
			};
			fRunning = true;
			thread.start();
			Toast.makeText(this, "Walking Detector service started", Toast.LENGTH_LONG).show();
		}
	}
	
	public synchronized void stopDetect() {
		if(fRunning) {
			fRunning = false;
			thread.interrupt();
			thread = null;
			Toast.makeText(this, "Walking Detector service stopped", Toast.LENGTH_LONG).show();
		}
	}
	
	private void updateStatus(long time, float acc, float dev) {
		timestamp = time;
		acceleration = acc;
		stddev = dev;
		if(acc >= threshold) {
			if(!fWalking) {
				walkingStart = time;
				fWalking = true;
			}
		} else {
			if(fWalking) {
				//tracker.addLog(new WalkingLog(walkingStart, time));
				fWalking = false;
			}
		}
		tracker.addLog(new WalkingLog(walkingStart, time));
	}
	
	public WalkingStatus getStatus() {
		return new WalkingStatus(timestamp, walkingStart, fWalking, acceleration, stddev);
	}
	
	public WalkingTracker getTracker() {
		return tracker;
	}

	public void onData(SensorData[] data, int dataCount) {
		Log.d(TAG, "onData");
		float[] ax = new float[dataCount];
		float[] ay = new float[dataCount];
		float[] az = new float[dataCount];
		for(int i = 0; i < dataCount; i++) {
			ax[i] = data[i].ax;
			ay[i] = data[i].ay;
			az[i] = data[i].az;
		}
		float[] fax = Statistics.hpFilter(ax, alpha);
		float[] fay = Statistics.hpFilter(ay, alpha);
		float[] faz = Statistics.hpFilter(az, alpha);
		float[] acc = new float[dataCount - 10];
		for(int i = 0; i < dataCount - 10; i++) {
			acc[i] = (float) Math.sqrt(fax[i + 10] * fax[i + 10]
					+ fay[i + 10] * fay[i + 10]
					+ faz[i + 10] * faz[i + 10]);
		}
		float mean = Statistics.average(acc);
		float stddev = Statistics.stdDev(acc, mean);
		updateStatus(data[0].timestamp, mean, stddev);
	}
	
	public void onDestroy() {
		Log.i(TAG, "onDestroy");
		stopDetect();
		if(fWalking) {
			tracker.addLog(new WalkingLog(walkingStart, System.currentTimeMillis()));
			fWalking = false;
		}
		tracker.persist();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}
}
